package com.sefer.dragonfly.client.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.modelmbean.ModelMBeanAttributeInfo;
import javax.management.modelmbean.ModelMBeanInfo;
import javax.management.modelmbean.ModelMBeanInfoSupport;
import javax.management.modelmbean.ModelMBeanOperationInfo;
import javax.management.modelmbean.RequiredModelMBean;

import org.apache.log4j.Logger;

import com.sefer.dragonfly.client.core.consumer.CmJvmConsumer;
import com.sefer.dragonfly.client.core.domain.mbean.annotation.CMbean;
import com.sefer.dragonfly.client.core.domain.mbean.annotation.CMbeanAttribute;
import com.sefer.dragonfly.client.core.domain.mbean.annotation.CMbeanMethod;
import com.sun.jdmk.comm.HtmlAdaptorServer;

public class MBeanAnnotationRegister {
	private static Logger logger = Logger
			.getLogger(MBeanAnnotationRegister.class);
	private static MBeanServer server = null;

	public static void addHtmlAdaptorServer(int port)
			throws MalformedObjectNameException, NullPointerException,
			InstanceAlreadyExistsException, MBeanRegistrationException,
			NotCompliantMBeanException {
		if (port == 0) {
			port = 9092;
		}
		HtmlAdaptorServer adapter = new HtmlAdaptorServer();
		ObjectName adaptorName = new ObjectName(
				"dragonfly:name=htmladaptor,port=" + port);
		adapter.setPort(port);
		getMBeanServer().registerMBean(adapter, adaptorName);
		adapter.start();
	}

	public static synchronized MBeanServer getMBeanServer() {
		long t1 = System.currentTimeMillis();

		if (server == null) {
			if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
				server = (MBeanServer) MBeanServerFactory.findMBeanServer(null)
						.get(0);
				if (logger.isDebugEnabled()) {
					logger.debug("Using existing MBeanServer "
							+ (System.currentTimeMillis() - t1));
				}
			} else {
				server = MBeanServerFactory.createMBeanServer();
				if (logger.isDebugEnabled()) {
					logger.debug("Creating MBeanServer"
							+ (System.currentTimeMillis() - t1));
				}
			}
		}
		return (server);
	}

	public static boolean unRegister(Object obj) {

		try {
			getMBeanServer()
					.unregisterMBean(
							new ObjectName(CommonUtils.getStrObjectName(obj
									.getClass())));
			return true;
		} catch (MBeanRegistrationException e) {
			logger.error(e.getMessage(), e);
		} catch (InstanceNotFoundException e) {
			logger.error(e.getMessage(), e);
		} catch (MalformedObjectNameException e) {
			logger.error(e.getMessage(), e);
		} catch (NullPointerException e) {
			logger.error(e.getMessage(), e);
		}
		return false;
	}

	/**
	 * 注册有mbean.annotation包中annotation标记的类
	 * 
	 * @param obj
	 * @return
	 */
	public static boolean register(Object obj) {
		MBeanServer server = getMBeanServer();
		ObjectName objectname = null;
		CMbean annotation = obj.getClass().getAnnotation(CMbean.class);
		if (annotation != null) {
			try {
				objectname = new ObjectName(CommonUtils.getStrObjectName(obj
						.getClass()));
			} catch (MalformedObjectNameException e) {
				logger.error(e.getMessage(), e);
				return false;
			} catch (NullPointerException e) {
				logger.error(e.getMessage(), e);
				return false;
			}

			RequiredModelMBean modelMbean = createModlerMBean(obj);
			try {
				return (server.registerMBean(modelMbean, objectname) != null);
			} catch (InstanceAlreadyExistsException e) {
				logger.warn(e.getMessage(), e);

				// 如果已经存在，则随机数拼装一下
				register(obj, CommonUtils.getStrObjectName(obj.getClass())
						+ (new Random()).nextInt(100));

			} catch (MBeanRegistrationException e) {
				logger.error(e.getMessage(), e);
			} catch (NotCompliantMBeanException e) {
				logger.error(e.getMessage(), e);
			}

		}
		return false;

	}

	/**
	 * 注册有mbean.annotation包中annotation标记的类
	 * 
	 * @param obj
	 * @return
	 */
	public static boolean register(Object obj, String strObjectname) {
		MBeanServer server = getMBeanServer();
		ObjectName objectname = null;
		CMbean annotation = obj.getClass().getAnnotation(CMbean.class);
		if (annotation != null) {
			try {
				objectname = new ObjectName(
						CommonUtils.getStrObjectName(strObjectname));
			} catch (MalformedObjectNameException e) {
				logger.error(e.getMessage(), e);
				return false;
			} catch (NullPointerException e) {
				logger.error(e.getMessage(), e);
				return false;
			}

			RequiredModelMBean modelMbean = createModlerMBean(obj);
			try {
				return (server.registerMBean(modelMbean, objectname) != null);
			} catch (InstanceAlreadyExistsException e) {
				logger.error(e.getMessage(), e);
			} catch (MBeanRegistrationException e) {
				logger.error(e.getMessage(), e);
			} catch (NotCompliantMBeanException e) {
				logger.error(e.getMessage(), e);
			}

		}
		return false;

	}

	private static RequiredModelMBean createModlerMBean(Object obj) {
		RequiredModelMBean model = null;
		try {
			model = new RequiredModelMBean();
			model.setManagedResource(obj, "ObjectReference");
			ModelMBeanInfo info = createModelMBeanInfo(obj);
			model.setModelMBeanInfo(info);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return model;
	}

	private static ModelMBeanInfo createModelMBeanInfo(Object obj) {
		ModelMBeanOperationInfo[] generateModelMBeanMethodInfo = generateModelMBeanMethodInfo(obj);
		ModelMBeanAttributeInfo[] generateModelMBeanAttributeInfo = generateModelMBeanAttributeInfo(obj);
		// create ModelMBeanInfo
		ModelMBeanInfo mbeanInfo = new ModelMBeanInfoSupport(//
				RequiredModelMBean.class.getName(), // MBean类
				obj.getClass().getName(), // 描述文字
				generateModelMBeanAttributeInfo,// 只有一个属性
				null, // 所有的构造函数信息
				generateModelMBeanMethodInfo,//
				null, // 所有的通知信息(本例无)
				null// MBean描述子
		);
		return mbeanInfo;
	}

	private static ModelMBeanOperationInfo[] generateModelMBeanMethodInfo(
			Object obj) {
		List<ModelMBeanOperationInfo> lists = new ArrayList<ModelMBeanOperationInfo>();
		Method[] methods = obj.getClass().getMethods();
		if (methods != null) {
			for (Method method : methods) {
				method.setAccessible(true);
				CMbeanMethod annotation = method
						.getAnnotation(CMbeanMethod.class);
				if (annotation != null) {

					Class<?>[] parameterTypes = method.getParameterTypes();
					MBeanParameterInfo[] signatures = null;
					if (parameterTypes != null && parameterTypes.length > 0) {
						signatures = new MBeanParameterInfo[parameterTypes.length];
						int i = 0;
						for (Class param : parameterTypes) {
							signatures[i] = new MBeanParameterInfo("args" + i,
									param.getName(), "");
						}
					}

					ModelMBeanOperationInfo info = new ModelMBeanOperationInfo(
							method.getName(), annotation.description(),
							signatures, method.getReturnType().getName(),
							annotation.impact());
					lists.add(info);
				}
			}
		}

		ModelMBeanOperationInfo[] infoArray = new ModelMBeanOperationInfo[lists
				.size()];

		lists.toArray(infoArray);

		return infoArray;
	}

	private static ModelMBeanAttributeInfo[] generateModelMBeanAttributeInfo(
			Object obj) {
		List<ModelMBeanAttributeInfo> lists = new ArrayList<ModelMBeanAttributeInfo>();
		Field[] fields = obj.getClass().getFields();
		if (fields != null) {
			for (Field field : fields) {
				field.setAccessible(true);
				CMbeanAttribute annotation = field
						.getAnnotation(CMbeanAttribute.class);
				if (annotation != null) {
					ModelMBeanAttributeInfo info = new ModelMBeanAttributeInfo(
							field.getName(), field.getType().getName(),
							annotation.description(), annotation.isReadable(),
							annotation.isWriteable(), annotation.isIs());
					lists.add(info);
				}
			}
		}

		ModelMBeanAttributeInfo[] infoArray = new ModelMBeanAttributeInfo[lists
				.size()];

		lists.toArray(infoArray);

		return infoArray;
	}

	public static String getType(ObjectName oname, String attName) {
		String type = null;
		MBeanInfo info;
		try {
			info = server.getMBeanInfo(oname);
		} catch (Exception e) {
			logger.info("Can't find metadata for object" + oname);
			return null;
		}

		MBeanAttributeInfo attInfo[] = info.getAttributes();
		for (int i = 0; i < attInfo.length; i++) {
			if (attName.equals(attInfo[i].getName())) {
				type = attInfo[i].getType();
				return type;
			}
		}
		return null;
	}

	public static Object convertValue(String type, String value) {
		Object objValue = value;

		if (type == null || "java.lang.String".equals(type)) {
			// string is default
			objValue = value;
		} else if ("javax.management.ObjectName".equals(type)
				|| "ObjectName".equals(type)) {
			try {
				objValue = new ObjectName(value);
			} catch (MalformedObjectNameException e) {
				return null;
			}
		} else if ("java.lang.Integer".equals(type) || "int".equals(type)) {
			objValue = new Integer(value);
		} else if ("java.lang.Long".equals(type) || "long".equals(type)) {
			objValue = new Long(value);
		} else if ("java.lang.Boolean".equals(type) || "boolean".equals(type)) {
			objValue = Boolean.valueOf(value);
		}
		return objValue;
	}

	public static void main(String[] args) {
		CmJvmConsumer jvm = new CmJvmConsumer();
		MBeanAnnotationRegister.register(jvm);

		try {
			MBeanInfo mBeanInfo = getMBeanServer().getMBeanInfo(
					new ObjectName(CommonUtils
							.getStrObjectName(CmJvmConsumer.class)));

			System.out.println(mBeanInfo.getClassName());
			System.out.println(mBeanInfo.getDescription());

			MBeanOperationInfo[] operations = mBeanInfo.getOperations();
			for (MBeanOperationInfo info : operations) {
				System.out.println("oper: " + info.getName() + " | "
						+ info.getDescription());
			}

			MBeanAttributeInfo[] attributes = mBeanInfo.getAttributes();
			for (MBeanAttributeInfo info : attributes) {
				System.out.println("attr: " + info.getName() + " | "
						+ info.getDescription());
			}
		} catch (IntrospectionException e) {

			e.printStackTrace();
		} catch (InstanceNotFoundException e) {

			e.printStackTrace();
		} catch (MalformedObjectNameException e) {

			e.printStackTrace();
		} catch (ReflectionException e) {

			e.printStackTrace();
		} catch (NullPointerException e) {

			e.printStackTrace();
		}
	}
}